package com.masnegocio.sellodigital.servicio.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

import org.apache.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;


import com.masnegocio.sellodigital.comun.Base64;
import com.masnegocio.sellodigital.comun.SelloDigitalConstantes;
import com.masnegocio.sellodigital.comun.SelloDigitalException;
import com.masnegocio.sellodigital.dao.KeyDao;
import com.masnegocio.sellodigital.dao.impl.KeyDaoImpl;
import com.masnegocio.sellodigital.dto.SelloDigitalDto;
import com.masnegocio.sellodigital.servicio.SelloDigitalServicio;

public class SelloDigitalServicioImpl implements SelloDigitalServicio {
	private static final Logger logger = Logger.getLogger(SelloDigitalServicioImpl.class);
	private KeyDao keyDao;
	
	@Override
	public String aplicarSelloDigital(SelloDigitalDto selloDigitalDto) throws SelloDigitalException{
		if(logger.isDebugEnabled()){
			logger.debug("INICIA EL PROCESO DE SELLADO");
		}
		PrivateKey privateKey = obtenerKey(selloDigitalDto.getRfc());
		String selloDigital = generarSelloDigital(selloDigitalDto.getCadenaOriginal(),privateKey);
		return selloDigital;
	}
	
	private String generarSelloDigital(String cadenaOriginal, PrivateKey privateKey)throws SelloDigitalException{
		Signature sign;
		try {
			sign = Signature.getInstance(SelloDigitalConstantes.ALGORITMO_SHA1WITHRSA);
			sign.initSign(privateKey);
			byte[] buffer=cadenaOriginal.getBytes();
			int length=buffer.length;
			sign.update(buffer,0,length);
			byte[] rawsigned=sign.sign();
			byte[] base64signed=Base64.encodeToByte(rawsigned);
			return new String(base64signed);
		} catch (NoSuchAlgorithmException e) {
			throw new SelloDigitalException(e,SelloDigitalConstantes.CODIGO_ERROR_ALGORITMO_NO_DISPONIBLE);
		} catch (InvalidKeyException e) {
			throw new SelloDigitalException(e,SelloDigitalConstantes.CODIGO_ERROR_LLAVE_INVALIDA);
		} catch (SignatureException e) {
			throw new SelloDigitalException(e,SelloDigitalConstantes.CODIGO_ERROR_LLAVE);
		}
		
	}
	
	@Override
	public PrivateKey obtenerKey(String rfc)throws SelloDigitalException{
		keyDao = new KeyDaoImpl(); //Quitar cuando se agregue Spring
		if(logger.isDebugEnabled()){
			logger.debug("LA RUTA A RECUPERAR CORRESPONDE AL RFC: "+rfc);
		}
		String rutaKey = keyDao.obtenerRutaKey(rfc);
		File fileKey = new File(rutaKey);
		FileInputStream privateKeyInputStream;
		Provider provider = null;
		try {
			privateKeyInputStream = new FileInputStream(fileKey);
			byte[] keyBytes = new byte[(int) fileKey.length()];
			privateKeyInputStream.read(keyBytes);
			privateKeyInputStream.close();
			provider = new BouncyCastleProvider();
			Security.insertProviderAt(provider,1);
			PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyBytes);			
			KeyFactory keyFactory = KeyFactory.getInstance(SelloDigitalConstantes.ALGORITMO_RSA);    		
			PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
			return privateKey;
		} catch (FileNotFoundException e) {
			logger.error("OCURRIO UNA EXCEPCION AL MOMENTAR DE INTENTAR RECUPERAR EL ARCHIVO QUE CONTIENE LA LLAVE "+e);
			throw new SelloDigitalException(e,SelloDigitalConstantes.CODIGO_ERROR_ARCHIVO_NO_EXISTE);
		} catch (IOException e) {
			logger.error("OCURRIO UNA EXCEPCION AL MOMENTAR DE INTENTAR LEER EL ARCHIVO QUE CONTIENE LA LLAVE "+e);
			throw new SelloDigitalException(e,SelloDigitalConstantes.CODIGO_ERROR_ARCHIVO_NO_DISPONIBLE);
		} catch (NoSuchAlgorithmException e) {
			logger.error("OCURRIO UNA EXCEPCION AL MOMENTAR DE INTENTAR APLICAR EL ALGORITMO "+SelloDigitalConstantes.ALGORITMO_RSA +" LA CUAL ES "+ e);
			throw new SelloDigitalException(e,SelloDigitalConstantes.CODIGO_ERROR_ALGORITMO_NO_DISPONIBLE);
		} catch (InvalidKeySpecException e) {
			logger.error("OCURRIO UNA EXCEPCION AL MOMENTAR DE INTENTAR GENERAR LA LLAVE PRIVADA "+ e);
			throw new SelloDigitalException(e,SelloDigitalConstantes.CODIGO_ERROR_ESPECIFICACION_INVALIDA);
		}finally{
			Security.removeProvider(provider.getName());
		}	
	}
	
}
